package com.ruoyi.learn.java.design.pattern.struct;

/**
 * 组合模式 (Composite Pattern) 示例：构建文件系统（文件和文件夹）。
 */
/**
 * 组合模式 (Composite Pattern) 示例：构建文件系统（文件和文件夹）。
 */
/**
 * 组合模式 (Composite Pattern) 示例：构建文件系统（文件和文件夹）。
 */
public class CompositePatternDemo {

    //region ========== 1. 组件 (Component) ==========

    /**
     * 组件：文件系统项
     * 定义了文件和文件夹的公共接口。
     * 客户端通过此接口与所有对象（叶子和组合）交互。
     */
    public static abstract class FileSystemItem {
        protected String name;

        public FileSystemItem(String name) {
            this.name = name;
        }

        // 公共操作：获取名称
        public String getName() {
            return name;
        }

        // 公共操作：打印信息（抽象方法，由子类实现）
        public abstract void displayInfo(int depth);

        // 公共操作：获取大小（抽象方法）
        public abstract long getSize();

        // --- 以下方法在 Component 中提供默认实现或抛出异常 ---
        // 因为只有文件夹（组合）才需要管理子节点。

        // 添加子节点（默认抛出异常，叶子节点不支持）
        public void add(FileSystemItem item) {
            throw new UnsupportedOperationException("不支持添加操作");
        }

        // 删除子节点（默认抛出异常，叶子节点不支持）
        public void remove(FileSystemItem item) {
            throw new UnsupportedOperationException("不支持删除操作");
        }

        // 获取子节点（默认返回 null 或空集合，叶子节点没有子节点）
        public FileSystemItem getChild(int index) {
            throw new UnsupportedOperationException("不支持获取子节点操作");
        }
    }

    //endregion

    //region ========== 2. 叶子 (Leaf) ==========

    /**
     * 叶子：文件
     * 表示文件系统中的基本对象，没有子节点。
     */
    public static class File extends FileSystemItem {
        private long size; // 文件大小（字节）

        public File(String name, long size) {
            super(name);
            this.size = size;
        }

        @Override
        public void displayInfo(int depth) {
            // 打印缩进和文件信息
            printIndent(depth);
            System.out.println("📄 文件: " + name + " (" + formatSize(size) + ")");
        }

        @Override
        public long getSize() {
            return size; // 文件大小就是其自身大小
        }

        // File 类不需要重写 add, remove, getChild，使用 Component 的默认异常实现即可。
    }

    //endregion

    //region ========== 3. 组合 (Composite) ==========

    /**
     * 组合：文件夹
     * 表示文件系统中的容器对象，可以包含文件和其他文件夹。
     */
    public static class Folder extends FileSystemItem {
        private java.util.List<FileSystemItem> children; // 存储子节点

        public Folder(String name) {
            super(name);
            this.children = new java.util.ArrayList<>();
        }

        // 重写 Component 的方法，支持管理子节点
        @Override
        public void add(FileSystemItem item) {
            children.add(item);
        }

        @Override
        public void remove(FileSystemItem item) {
            children.remove(item);
        }

        @Override
        public FileSystemItem getChild(int index) {
            if (index >= 0 && index < children.size()) {
                return children.get(index);
            }
            throw new IndexOutOfBoundsException("索引超出范围: " + index);
        }

        @Override
        public void displayInfo(int depth) {
            // 打印缩进和文件夹信息
            printIndent(depth);
            System.out.println("📁 文件夹: " + name);

            // 递归地调用所有子节点的 displayInfo 方法
            for (FileSystemItem child : children) {
                child.displayInfo(depth + 1); // 增加缩进层级
            }
        }

        @Override
        public long getSize() {
            long totalSize = 0;
            // 递归地累加所有子节点的大小
            for (FileSystemItem child : children) {
                totalSize += child.getSize(); // 无论是文件还是文件夹，都调用 getSize()
            }
            return totalSize;
        }
    }

    //endregion

    //region ========== 4. 工具方法 ==========

    /**
     * 打印指定深度的缩进
     */
    private static void printIndent(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("  "); // 两个空格代表一层缩进
        }
    }

    /**
     * 格式化文件大小（字节 -> KB, MB, GB）
     */
    private static String formatSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        double kb = bytes / 1024.0;
        if (kb < 1024) return String.format("%.2f KB", kb);
        double mb = kb / 1024.0;
        if (mb < 1024) return String.format("%.2f MB", mb);
        double gb = mb / 1024.0;
        return String.format("%.2f GB", gb);
    }

    //endregion

    //region ========== 5. 客户端 (Client) ==========

    /**
     * 文件系统浏览器客户端
     * 可以统一地操作文件和文件夹。
     */
    public static class FileSystemBrowser {
        public void browse(FileSystemItem root) {
            System.out.println("【客户端】开始浏览文件系统...");
            root.displayInfo(0); // 从根节点开始，深度为0
            System.out.println("【客户端】浏览完成。");
        }

        public void showSize(FileSystemItem item) {
            long size = item.getSize();
            System.out.println("【客户端】'" + item.getName() + "' 的总大小: " + formatSize(size));
        }
    }

    //endregion

    //region ========== 6. 主函数 (Main) - 演示组合模式 ==========

    public static void main(String[] args) {
        System.out.println("=== 组合模式演示 ===\n");

        // 1. 创建叶子节点（文件）
        System.out.println("1. 创建文件（叶子节点）...");
        File file1 = new File("文档.txt", 2048); // 2KB
        File file2 = new File("图片.jpg", 1048576); // 1MB
        File file3 = new File("视频.mp4", 1073741824); // 1GB
        File file4 = new File("脚本.py", 512);

        // 2. 创建组合节点（文件夹）并构建树形结构
        System.out.println("2. 创建文件夹（组合节点）并构建树形结构...\n");

        // 根文件夹
        Folder root = new Folder("我的电脑");

        // 创建子文件夹
        Folder documents = new Folder("文档");
        Folder pictures = new Folder("图片");
        Folder projects = new Folder("项目");

        // 将文件添加到对应的文件夹
        documents.add(file1); // 文档.txt -> 文档文件夹
        pictures.add(file2);  // 图片.jpg -> 图片文件夹
        projects.add(file4);  // 脚本.py -> 项目文件夹

        // 将子文件夹添加到根文件夹
        root.add(documents);
        root.add(pictures);
        root.add(projects);

        // 在项目文件夹中再创建一个子文件夹
        Folder webProject = new Folder("Web应用");
        webProject.add(file3); // 视频.mp4 -> Web应用文件夹 (假设是教程视频)
        projects.add(webProject); // Web应用 -> 项目文件夹

        // 3. 客户端使用组合模式
        System.out.println("3. 客户端操作文件系统...\n");
        FileSystemBrowser browser = new FileSystemBrowser();

        // --- 操作 1: 统一浏览整个结构 ---
        browser.browse(root);
        System.out.println();

        // --- 操作 2: 统一获取大小 ---
        browser.showSize(file1); // 获取单个文件大小
        browser.showSize(pictures); // 获取图片文件夹大小
        browser.showSize(webProject); // 获取 Web应用 文件夹大小
        browser.showSize(root); // 获取整个根目录大小
        System.out.println();

        // 4. 演示动态修改结构
        System.out.println("4. 演示：动态添加新文件...");
        File newFile = new File("新文档.docx", 4096);
        documents.add(newFile); // 将新文件添加到文档文件夹
        System.out.println("    > 已将 '新文档.docx' 添加到 '文档' 文件夹。");
        browser.showSize(documents); // 查看添加后文档文件夹的大小
        System.out.println();

        // 5. 对比：如果不使用组合模式
        System.out.println("--- 对比：不使用组合模式 ---");
        System.out.println("如果不用组合模式，客户端需要：");
        System.out.println("  - 区分对象是文件还是文件夹。");
        System.out.println("  - 对文件夹要遍历其子节点，对文件直接操作。");
        System.out.println("  - 每次操作（如显示、计算大小）都需要写条件判断。");
        System.out.println("代码会变得复杂且难以维护。");
        System.out.println("而使用组合模式，客户端代码完全透明，无需关心对象类型。");

        // 6. 注意事项
        System.out.println("\n--- 注意事项 ---");
        System.out.println("• 在 Component 接口中，对于叶子节点不支持的操作（如 add, remove），");
        System.out.println("  可以选择抛出异常（如本例），或者返回默认值（如返回 null 或 false）。");
        System.out.println("• 组合模式可能会产生一个复杂的树结构，需要谨慎管理内存和避免循环引用。");
    }

    //endregion
}